In [171]:
import os
import pandas as pd
import numpy as np
import json
import datetime

import matplotlib.pyplot as plt
import plotly
import plotly.graph_objects as go
from plotly.subplots import make_subplots
plotly.offline.init_notebook_mode()
%load_ext autoreload
%autoreload 2
%config InlineBackend.figure_format = 'retina'

import chart_utils
import analysis_utils
The autoreload extension is already loaded. To reload it, use:
  %reload_ext autoreload
In [172]:
# try:
#     os.remove("btc.csv")
# except FileNotFoundError:
#     pass
# !wget https://coinmetrics.io/newdata/btc.csv
In [173]:
data = pd.read_csv("btc.csv").dropna()
data = analysis_utils.get_extra_datetime_cols(data, 'date')
data.columns.values
Out[173]:
array(['date', 'AdrActCnt', 'BlkCnt', 'BlkSizeByte', 'BlkSizeMeanByte',
       'CapMVRVCur', 'CapMrktCurUSD', 'CapRealUSD', 'DiffMean',
       'FeeMeanNtv', 'FeeMeanUSD', 'FeeMedNtv', 'FeeMedUSD', 'FeeTotNtv',
       'FeeTotUSD', 'HashRate', 'IssContNtv', 'IssContPctAnn',
       'IssContUSD', 'IssTotNtv', 'IssTotUSD', 'NVTAdj', 'NVTAdj90',
       'PriceBTC', 'PriceUSD', 'ROI1yr', 'ROI30d', 'SplyCur',
       'SplyExpFut10yrCMBI', 'SplyFF', 'TxCnt', 'TxTfrCnt',
       'TxTfrValAdjNtv', 'TxTfrValAdjUSD', 'TxTfrValMeanNtv',
       'TxTfrValMeanUSD', 'TxTfrValMedNtv', 'TxTfrValMedUSD',
       'TxTfrValNtv', 'TxTfrValUSD', 'VtyDayRet180d', 'VtyDayRet30d',
       'VtyDayRet60d', 'datetime', 'year', 'month', 'week', 'rhr_week',
       'day', 'halving_era', 'market_cycle'], dtype=object)
In [174]:
def get_halving_era_string(row):
    if row['halving_era'] == datetime.date(2009, 1, 3):
        return 'The before Time'
    elif row['halving_era'] == datetime.date(2012, 11, 29):
        return '2013 Cycle'
    elif row['halving_era'] == datetime.date(2016, 7, 10):
        return '2017 Cycle'
    elif row['halving_era'] == datetime.date(2020, 5, 11):
        return '2021 Cycle'
    return 'Other'

def get_halving_era_string(row):
    if row['market_cycle'] == datetime.date(2009, 1, 3):
        return 'The before Time'
    elif row['market_cycle'] == datetime.date(2011, 11, 18):
        return '2013 Cycle'
    elif row['market_cycle'] == datetime.date(2015, 1, 14):
        return '2017 Cycle'
    elif row['market_cycle'] == datetime.date(2018, 12, 16):
        return '2021 Cycle'
    return 'Other'
In [175]:
data['days_since_halving'] = [(datetime.datetime.strptime(x, "%Y-%m-%d").date() - y).days for x, y in zip(data['day'], data['halving_era'])]
data['halving_era_string'] = data.apply(get_halving_era_string, axis=1)

data['days_since_cycle'] = [(datetime.datetime.strptime(x, "%Y-%m-%d").date() - y).days for x, y in zip(data['day'], data['market_cycle'])]
data['cycle_string'] = data.apply(get_halving_era_string, axis=1)
In [176]:
def plot_comparison_chart(df, series, index=False, cycle='halving', **kwargs):
    if cycle == 'halving':
        x_series = 'days_since_halving'
        cols = 'halving_era_string'
        x_title = 'Days Since Halving'
        cutoff = 600
    else:
        x_series = 'days_since_cycle'
        cols = 'cycle_string'
        x_title = 'Days Since Cycle Bottom'
        cutoff = 2000
        
    temp = df.loc[
        df[x_series] <= cutoff
    ].pivot(index=x_series, columns=cols, values=series).reset_index(drop=True)
    
    if index == True:
        for col in temp.columns:
            temp[col] = temp[col] / temp[col][0]

    fig = make_subplots(
        specs=[[{"secondary_y": False}]]
    )

    fig.add_trace(
        go.Scatter(x=temp.index, y=temp['2013 Cycle'], name='2013 Cycle'),
        secondary_y=False
    )

    fig.add_trace(
        go.Scatter(x=temp.index, y=temp['2017 Cycle'], name='2017 Cycle'),
        secondary_y=False
    )

    fig.add_trace(
        go.Scatter(x=temp.index, y=temp['2021 Cycle'], name='2021 Cycle'),
        secondary_y=False
    )

    fig.update_layout(
        title_text=kwargs.get('title', series),
        annotations=[
            dict(x=1, y=-0.1,
                 text="Chart by: @typerbole; Data: {}".format(kwargs.get('data_source', 'CoinMetrics')),
                 showarrow=False, xref='paper', yref='paper',
                 xanchor='right', yanchor='auto', xshift=0, yshift=0)
        ],
        showlegend=True,
        legend_orientation="h",
        template="plotly_dark"

    )
    
    fig.update_xaxes(
        title_text=x_title, showgrid=False, showline=False, zeroline=False)
    fig.update_yaxes(
        title_text=series, showgrid=False, showline=False, zeroline=False,
        tickformat=kwargs.get('y_axis_format', None),
        type=kwargs.get('y_axis_type', 'linear'), range=kwargs.get('y_axis_range'),)

    return fig.show()
In [177]:
data.columns.values
Out[177]:
array(['date', 'AdrActCnt', 'BlkCnt', 'BlkSizeByte', 'BlkSizeMeanByte',
       'CapMVRVCur', 'CapMrktCurUSD', 'CapRealUSD', 'DiffMean',
       'FeeMeanNtv', 'FeeMeanUSD', 'FeeMedNtv', 'FeeMedUSD', 'FeeTotNtv',
       'FeeTotUSD', 'HashRate', 'IssContNtv', 'IssContPctAnn',
       'IssContUSD', 'IssTotNtv', 'IssTotUSD', 'NVTAdj', 'NVTAdj90',
       'PriceBTC', 'PriceUSD', 'ROI1yr', 'ROI30d', 'SplyCur',
       'SplyExpFut10yrCMBI', 'SplyFF', 'TxCnt', 'TxTfrCnt',
       'TxTfrValAdjNtv', 'TxTfrValAdjUSD', 'TxTfrValMeanNtv',
       'TxTfrValMeanUSD', 'TxTfrValMedNtv', 'TxTfrValMedUSD',
       'TxTfrValNtv', 'TxTfrValUSD', 'VtyDayRet180d', 'VtyDayRet30d',
       'VtyDayRet60d', 'datetime', 'year', 'month', 'week', 'rhr_week',
       'day', 'halving_era', 'market_cycle', 'days_since_halving',
       'halving_era_string', 'days_since_cycle', 'cycle_string'],
      dtype=object)
In [178]:
plot_comparison_chart(data, 'CapMrktCurUSD', cycle='cycle', index=True, title='Market Cap', y_axis_type='log')
In [179]:
plot_comparison_chart(data, 'HashRate', cycle='halving', index=True, title='Hash Rate', y_axis_type='log')
In [180]:
plot_comparison_chart(data, 'CapMVRVCur', cycle='halving', index=False, title='MVRV')
In [181]:
plot_comparison_chart(data, 'TxTfrValAdjUSD', cycle='halving', index=True, title='On-Chain Transaction Volume', y_axis_type='log')
In [182]:
plot_comparison_chart(data, 'VtyDayRet30d', cycle='halving', index=False, title='30 Day Trailing Volatility')
In [183]:
plot_comparison_chart(data, 'AdrActCnt', cycle='halving', index=True, title='Active OnChain Addresses')
In [184]:
supply = pd.read_csv("~/Downloads/liquid-and-illiquid-supply.csv")
In [185]:
supply['date'] = [x[:10] for x in supply['timestamp']]
In [186]:
supply = analysis_utils.get_extra_datetime_cols(supply, 'date')
supply['days_since_halving'] = [(datetime.datetime.strptime(x, "%Y-%m-%d").date() - y).days for x, y in zip(supply['day'], supply['halving_era'])]
supply['halving_era_string'] = supply.apply(get_halving_era_string, axis=1)

supply['days_since_cycle'] = [(datetime.datetime.strptime(x, "%Y-%m-%d").date() - y).days for x, y in zip(supply['day'], supply['market_cycle'])]
supply['cycle_string'] = supply.apply(get_halving_era_string, axis=1)
supply.columns.values
Out[186]:
array(['timestamp', 'highly_liquid', 'illiquid', 'liquid', 'date',
       'datetime', 'year', 'month', 'week', 'rhr_week', 'day',
       'halving_era', 'market_cycle', 'days_since_halving',
       'halving_era_string', 'days_since_cycle', 'cycle_string'],
      dtype=object)
In [187]:
supply['total_supply'] = supply['highly_liquid'] + supply['illiquid'] + supply['liquid']
supply['illiquid_pct'] = supply['illiquid'] / supply['total_supply']
In [188]:
plot_comparison_chart(
    supply, 'illiquid_pct', cycle='halving', index=False, title='Illiquid Supply Percent', 
    data_source='GlassNode', y_axis_format=".0%")
In [189]:
realcap = pd.read_csv("~/git/bitcoindash/realcap.csv")
realcap = analysis_utils.get_extra_datetime_cols(realcap, 'date')
realcap['days_since_halving'] = [(datetime.datetime.strptime(x, "%Y-%m-%d").date() - y).days for x, y in zip(realcap['day'], realcap['halving_era'])]
realcap['halving_era_string'] = realcap.apply(get_halving_era_string, axis=1)

realcap['days_since_cycle'] = [(datetime.datetime.strptime(x, "%Y-%m-%d").date() - y).days for x, y in zip(realcap['day'], realcap['market_cycle'])]
realcap['cycle_string'] = realcap.apply(get_halving_era_string, axis=1)
realcap.columns.values
Out[189]:
array(['date', 'block_number', 'block_ts', 'price_usd',
       'total_utxo_value', 'utxo_value_under_1d', 'utxo_value_1d_1w',
       'utxo_value_1w_1m', 'utxo_value_1m_3m', 'utxo_value_3m_6m',
       'utxo_value_6m_12m', 'utxo_value_12m_18m', 'utxo_value_18m_24m',
       'utxo_value_2y_3y', 'utxo_value_3y_5y', 'utxo_value_5y_8y',
       'utxo_value_greater_8y', 'total_utxo_count', 'utxo_count_under_1d',
       'utxo_count_1d_1w', 'utxo_count_1w_1m', 'utxo_count_1m_3m',
       'utxo_count_3m_6m', 'utxo_count_6m_12m', 'utxo_count_12m_18m',
       'utxo_count_18m_24m', 'utxo_count_2y_3y', 'utxo_count_3y_5y',
       'utxo_count_5y_8y', 'utxo_count_greater_8y',
       'total_utxo_count_filter', 'utxo_count_filter_under_1d',
       'utxo_count_filter_1d_1w', 'utxo_count_filter_1w_1m',
       'utxo_count_filter_1m_3m', 'utxo_count_filter_3m_6m',
       'utxo_count_filter_6m_12m', 'utxo_count_filter_12m_18m',
       'utxo_count_filter_18m_24m', 'utxo_count_filter_2y_3y',
       'utxo_count_filter_3y_5y', 'utxo_count_filter_5y_8y',
       'utxo_count_filter_greater_8y', 'realized_cap',
       'utxo_realcap_under_1d', 'utxo_realcap_1d_1w',
       'utxo_realcap_1w_1m', 'utxo_realcap_1m_3m', 'utxo_realcap_3m_6m',
       'utxo_realcap_6m_12m', 'utxo_realcap_12m_18m',
       'utxo_realcap_18m_24m', 'utxo_realcap_2y_3y', 'utxo_realcap_3y_5y',
       'utxo_realcap_5y_8y', 'utxo_realcap_greater_8y', 'datetime',
       'year', 'month', 'week', 'rhr_week', 'day', 'halving_era',
       'market_cycle', 'days_since_halving', 'halving_era_string',
       'days_since_cycle', 'cycle_string'], dtype=object)
In [190]:
realcap['pct_realcap_under_3m'] = (
    realcap['utxo_realcap_under_1d'] + realcap['utxo_realcap_1d_1w'] +
    realcap['utxo_realcap_1w_1m'] + realcap['utxo_realcap_1m_3m']
) / realcap['realized_cap']
In [192]:
plot_comparison_chart(
    realcap, 'pct_realcap_under_3m', cycle='halving', index=False, title='Percent of Realized Cap Created in Past 3 Months', 
    data_source='proprietary', y_axis_format=".0%")
In [ ]: